package cn.jofei.common.utils;

import cn.jofei.common.config.AppProperties;
import cn.jofei.common.netty.message.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.temporal.Temporal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * @author bao
 * @version my-web v1.0
 * @description 对象工具类
 * @date 2022/2/22 16:30
 */
public class Utils {

    public static final Logger log = LoggerFactory.getLogger(Utils.class);

    private static final String[] chars = new String[]{"a", "b", "c", "d", "e", "f",
            "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
            "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
            "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
            "W", "X", "Y", "Z", "/", "*", "-", "+", "?", "!", "@", "#", "$", "%", "&", "*"};

    private static final String IP_REGEX = "^\\d{1,3}.\\d{1,3}.\\d{1,3}.\\d{1,3}";

    public static final String AES_KEY = AppProperties.get("secret.aes-key",String.class).orElse("/O0*+rHLW$LpQSb&GWuw?Q");

    public static Mac MAC;

    static {
        try {
            MAC = Mac.getInstance("HmacSHA1");
            MAC.init(new SecretKeySpec(AES_KEY.getBytes(StandardCharsets.ISO_8859_1), "HmacSHA1"));
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            log.error("MAC 初始化异常", e);
        }
    }

    private Utils() {
        throw new AssertionError("ObjUtils cannot be instantiated!");
    }

    public static boolean isWin() {
        return System.getProperty("os.name").contains("Windows");
    }

    /**
     * 判断空
     *
     * @param pObj
     * @return
     */
    public static boolean isNull(Object pObj) {
        if (pObj == null) {
            return true;
        } else {
            if (pObj instanceof String) {
                if (((String) pObj).length() == 0) {
                    return true;
                }
            } else if (pObj instanceof Collection) {
                if (((Collection) pObj).size() == 0) {
                    return true;
                }
            } else if (pObj instanceof Map && ((Map) pObj).size() == 0) {
                return true;
            }
            return false;
        }
    }

    /**
     * 判断非空
     *
     * @param pObj
     * @return
     */
    public static boolean nonNull(Object pObj) {
        return !isNull(pObj);
    }

    /**
     * 流水号生成方法
     *
     * @return
     */
    public static String uid() {
        StringBuilder shortBuffer = new StringBuilder();
        String uuid = System.currentTimeMillis() + UUID.randomUUID().toString().replace("-", "");
        for (int i = 0; i < 15; i++) {
            String str = uuid.substring(i * 3, i * 3 + 3);
            int x = Integer.parseInt(str, 16);
            shortBuffer.append(chars[x % 0x4A]);
        }
        return shortBuffer.toString();
    }

    public static void main(String[] args) {
        System.out.println(uid());
        System.out.println(uid());
        System.out.println(uid());
    }

    private static final AtomicInteger seq = new AtomicInteger();

    /**
     * 流水号生成方法
     *
     * @return
     */
    public static Integer sequenceId() {
        return seq.incrementAndGet();
    }

    /**
     * 加密
     *
     * @param source
     * @return
     */
    public static byte[] encode(String source) {
        return encode(source.getBytes(StandardCharsets.UTF_8));
    }

    public static byte[] encode(byte[] source) {
        return Base64.getEncoder().encode(source);
    }

    /**
     * 解密
     *
     * @param bytes
     * @return
     */
    public static String decode(byte[] bytes) {
        if (bytes != null && bytes.length > 0) {
            try {
                return new String(Base64.getDecoder().decode(bytes), StandardCharsets.UTF_8);
            } catch (Exception e) {
                log.error("Base64解密异常", e);
            }
        }
        return null;
    }

    public static String decode(String str) {
        if (str != null && str.length() > 0) {
            try {
                return new String(Base64.getDecoder().decode(str), StandardCharsets.UTF_8);
            } catch (Exception e) {
                log.error("Base64解密异常", e);
            }
        }
        return null;
    }

    /**
     * 获取盐值加密码
     *
     * @param str
     * @return
     */
    public static String getSecret(String str) {
        if (str == null){
            return "";
        }
        byte[] text = str.getBytes(StandardCharsets.ISO_8859_1);
        byte[] bytes = MAC.doFinal(text);
        return new String(encode(bytes), StandardCharsets.UTF_8);
    }

    /**
     * 添加 synchronized 并发报错
     *
     * @param message
     * @return
     */
    public synchronized static String getSecret(Message message) {
        if (message == null){
            return "";
        }
        StringBuilder builder = new StringBuilder();
        builder.append(message.getSequenceId());
        builder.append(message.getMsgType());
        builder.append(message.getTime());
        builder.append(message.getSubdomain());
        builder.append(message.getPort());
        byte[] bytes = MAC.doFinal(builder.toString().getBytes(StandardCharsets.ISO_8859_1));
        return new String(encode(bytes), StandardCharsets.UTF_8);
    }

    public static boolean isIp(String host) {
        if (isNull(host)) {
            return false;
        }
        return host.matches(IP_REGEX);
    }

    /**
     * 判断是否为数字
     */
    private static final Pattern NUM_PATTERN = Pattern.compile("^[-\\+]?[\\d]*$");

    public static boolean isNum(String str) {
        return NUM_PATTERN.matcher(str).matches();
    }

    /**
     * gzip压缩
     *
     * @param data
     * @return
     */
    public static byte[] gZip(byte[] data) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip = null;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(data);
            gzip.close();
            return out.toByteArray();
        } catch (IOException e) {
            log.error("gzip压缩失败！", e);
            return null;
        } finally {
            Streams.safeClose(out);
            Streams.safeClose(gzip);
        }
    }

    /**
     * gzip解压缩
     *
     * @param decode
     * @return
     */
    public static byte[] unGzip(byte[] decode) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = null;
        GZIPInputStream gzipStream = null;
        try {
            in = new ByteArrayInputStream(decode);
            gzipStream = new GZIPInputStream(in);
            byte[] buffer = new byte[256];
            while (true) {
                int offset = -1;
                try {
                    offset = gzipStream.read(buffer);
                } catch (EOFException ex) {
                }
                if (offset != -1) {
                    out.write(buffer, 0, offset);
                } else {
                    break;
                }
            }
            return out.toByteArray();
        } catch (IOException e) {
            log.error("gzip解压缩失败！" + new String(decode), e);
            return null;
        } finally {
            Streams.safeClose(out);
            Streams.safeClose(in);
            Streams.safeClose(gzipStream);
        }
    }

    /**
     * 创建文件
     *
     * @param file 文件
     * @return 父级文件夹
     */
    public static File createFile(File file) {
        File parentFile = file.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        } else if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                log.error("创建文件失败", e);
            }
        }
        return parentFile;
    }

    public static boolean match(Message message) {
        return message != null && message.getCode() != null && message.getCode().equals(getSecret(message));
    }

    /**
     * 是否为基本类型
     *
     * @param obj
     * @return
     */
    public static boolean isBasicType(Object obj) {
        try {
            return ((Class) obj.getClass().getField("TYPE").get(null)).isPrimitive();
        } catch (Exception e) {
            return false;
        }
    }

    private static final List<Class<?>> MIN_CLAZZ_LIST = Arrays.asList(String.class, Date.class, Temporal.class);

    /**
     * 是否为最小类型
     *
     * @param obj
     * @return
     */
    public static boolean isMinType(Class obj) {
        if (isNull(obj)) {
            return false;
        }
        if (isBasicType(obj)) {
            return true;
        } else {
            return MIN_CLAZZ_LIST.stream().anyMatch(e -> e.isAssignableFrom(obj));
        }
    }
}